import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;

import org.jgrapht.alg.ConnectivityInspector;
import org.jgrapht.graph.DefaultWeightedEdge;
import org.jgrapht.graph.UndirectedWeightedSubgraph;

/***********************************************************************************8
 * Class Cluster
 * @author liat
 * Description: Cluster is a collection of nodes.
 * Last updater: Liat
 * Date: 6.08.09 18:00
 */

public class Cluster implements Iterable<Gene> {
	
	private final boolean SUCCESS = true;
	private final boolean FAILURE = false;
	private LinkedList<Gene> _members;
	private String _annotation;
	
	/**
	 * Generates a cluster from a given list of nodes.
	 */
	public Cluster(LinkedList<Gene> members) {
		_members = members;
		_annotation = null;
		for (Gene n: members) {
			n.addCluster(this);
		}
	}
	
	/**
	 * This constructor builds an empty cluster.
	 */
	public Cluster() {
		_members = new LinkedList<Gene>();
	}
	
	/**
	 * @return Amount of members.
	 */
	public int size() {
		return _members.size();
	}

	/* Annotations - related methods */
	
	/**
	 * Count how many cluster members are annotated.
	 * @return amount of annotated clusters members.
	 */
	public int countAnnotatedGenes() {
		int count = 0;
		for (Gene g: _members) {
			if (g.isAnnotated()) {
				++count;
			}
		}
		
		return count;
	}
	
	public boolean hasAnnotatedGenes() {
		for (Gene g: _members) {
			if (g.isAnnotated())
				return true;
		}
		
		return false;
	}
	
	public int countGenesWithDominantAnnotation(AnnotationsManager annotMgr) {
		if (!isAnnotated())
			return 0;
		
		int result = 0;
		for (Gene g: _members) {
			if (annotMgr.isGeneAnnotated(g, _annotation))
				++result;
		}
		
		return result;
	}
	
	/**
	 * @return the dominant annotation determined for the cluster, or null if there is none.
	 */
	public String getAnnotation() {
		return _annotation;
	}
	
	/**
	 * @return true iff the cluster was assigned a significant dominant annotation. 
	 */
	public boolean isAnnotated() {
		return _annotation != null;
	}
	
	/**
	 * Set the cluster's dominant annotation. 
	 * @param annot null if cluster is not an annotation, a standard GO annotation name otherwise.
	 */
	public void setAnnotation(String annot) {
		_annotation = annot;
	}
	
	/* Members -related functions */
	/**
	 * Add a node to the cluster
	 * @param node The node to add to the cluster.
	 * @return true if the node was inserted to the cluster.
	 * false otherwise.
	 */
	public boolean add(Gene g) {
		if (g == null)
			return FAILURE;

		g.addCluster(this);
		return _members.add(g);
	}

	/**
	 * Removes a node from the cluster
	 * @param g The gene to remove from the cluster.
	 * @return true if the node was removed to the cluster.
	 * false otherwise.
	 */	
	public boolean remove(Gene g) {
		if (!_members.contains(g))
			return FAILURE;
		else {
			_members.remove(g);
			g.removeCluster(this);
		}
		return SUCCESS;
	}	
	
	/**
	 * Remove all nodes from cluster.
	 * Note - does not use Cluster::remove(node) due to efficiency considerations, but has the same consequence.
	 */
	public void clear() {
		for (Gene g: _members) {
			g.removeCluster(this);
		}
		_members.clear();
	}
	
	/**
	 * @param g, the gene to check for its existence
	 * in the cluster.
	 * @return true if the node is in the cluster
	 * false otherwise.
	 */
	public boolean contains(Gene g) {
		return _members.contains(g);
	}

	
	/**
	 * @return An iterator over all of the cluster's genes.
	 */
	public Iterator<Gene> iterator() {
		return _members.iterator();
	}
	
	public HashSet<String> memberNames() {
		HashSet<String> names = new HashSet<String>(_members.size());
		for (Gene g: _members) {
			names.add(g.getName());
		}
		
		return names;
	}
	
	/**
	 * Returns a string representation of the cluster.
	 */
	public String toString() {
		String str = "";
		for (int i=0; i< _members.size(); i++) {
			str += _members.get(i).getName();
			if (i != _members.size()-1) {
				str += " ";
			}
		}
		return str;
	}
	
	public String outputFileText() {
			return _annotation + "\t" + toString();
	}
}
